/**
*
 * Preemptive Least Laxity First
 * 04/02
* see <a href="{@docRoot}/Documentation/scheduler.html"> for more information</a>
*  Copyright (c) 2009 Ecole Centrale de Nantes IRCCyN
* @author: amd
* @version 3.2
*/
package storm.Schedulers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import storm.EvtContext;
import storm.Processors.Processor;
import storm.Tasks.Task;




/**
 * 
 * @author AMD
 *
 */
public class LLF_P_Scheduler extends Scheduler {
	class LReady extends ArrayList implements Comparator {
		/**
		 * 
		 */
		public int compare(Object arg0, Object arg1) {
			Task T0 = (Task) arg0;
			Task T1 = (Task) arg1;
			int d0 = T0.getOwnFieldIntValue("laxity");
			int d1 = T1.getOwnFieldIntValue("laxity");
			if (d1 > d0) return -1;
			else if (d1 == d0) return 0;
			     else return 1;
		}
		
	}
	/**
	 * 
	 */
	private LReady list_ready;
	private Boolean todo = false;
	private boolean tocompute = false;
	private int nbTicks = 0;
	/**
	 * 
	 */
	public void init() {
		super.init();
		list_ready = new LReady();
	}
	/**
	 * 
	 */
	public void onActivate(EvtContext c) {
		Task T = (Task)c.getCible();
		/**
		 * calcul de la prochaine date d'échéance
		 */
		T.setOwnFieldIntValue("next_deadline", this.Kernel.getTime()+T.getDeadline());
		int d = T.getOwnFieldIntValue("next_deadline");
		/**
		 * calcul du temps restant jusqu'a la prochaine date d'échéance
		 */
		int l = d-this.Kernel.getTime();
		/**
		 * calcul de la laxite dynamique
		 */
		T.setOwnFieldIntValue("laxity", l-T.getRET());
		list_ready.add(T);
		todo = true;
		tocompute = true;
	}
	/**
	 * 
	 */
	public void onUnReady(EvtContext c){
		this.onUnBlock(c);
	}
	/**
	 * 
	 */
	public void onUnBlock(EvtContext c){
		Task T = (Task)c.getSource();
		if (T.isBegin()) {
			/**
			 * calcul de la prochaine date d'échéance
			 */
			T.setOwnFieldIntValue("next_deadline", this.Kernel.getTime()+T.getDeadline());
			int d = T.getOwnFieldIntValue("next_deadline");
			/**
			 * calcul du temps restant jusqu'a la prochaine date d'échéance
			 */
			int l = d-this.Kernel.getTime();
			/**
			 * calcul de la laxite dynamique
			 */
			T.setOwnFieldIntValue("laxity", l-T.getRET());
		}
		list_ready.add(T);
		todo = true;
		tocompute = true;
	}
	/**
	 * 
	 */
	public void onBlock(EvtContext c){
		list_ready.remove(c.getCible());
		todo = true;
	}
	/**
	 * 
	 */
	public void onTerminated(EvtContext c){
		list_ready.remove(c.getCible());
		todo = true;
	}
	/**
	 * 
	 */
	public void onTick(){
		int q = this.getOwnFieldIntValue("quantum");
		nbTicks = (nbTicks+1)%q;
		if (nbTicks == 0) {
			tocompute = true;
			todo = true;
		}
	}
	/**
	 * 
	 */
	public void sched(){
		
		if (tocompute) {
			laxityCompute();
			tocompute = false;
		}
		if (todo) {
			select();
			todo = false;
		}
	}
	/**
	 * 
	 */
	private void laxityCompute(){
		for (int i=0; i<list_ready.size(); i++){
			Task T = (Task) list_ready.get(i);
			/**
			 * récupération de la prochaine date d'échéance
			 */
			int d = T.getOwnFieldIntValue("next_deadline");
			/**
			 * calcul du temps restant jusqu'a la prochaine date d'échéance
			 */
			int l = d-this.Kernel.getTime();
			/**
			 * calcul de la laxite dynamique
			 */
			T.setOwnFieldIntValue("laxity", l-T.getRET());
		}
	}
	/**
	 * 
	 */
	public void select() {
		Collections.sort(list_ready,list_ready);
		for (int i=0; i<list_ready.size(); i++){
			Task T = (Task) list_ready.get(i);
		}
		ArrayList CPUS =this.Kernel.getTasksListeManager().getProcessors();
		int m = CPUS.size();
		for (int i=m; i<list_ready.size(); i++){
			Task T = (Task) list_ready.get(i);
			if (T.isIsrunning()) T.preempt();
		}
		int j = 0;
		for (int i=0; (i<m) && (i<list_ready.size()); i++){
			Task T = (Task) list_ready.get(i);
			if (!T.isIsrunning()) {
				Processor P = null;
				for (; j<m; j++){
					P = (Processor) CPUS.get(j);
					if (!P.isRunning()) {
						j++;
						break;
					}
				}
				T.runningOn(P);
			}
		}

	}

}
